﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WebLog.Db
{
    internal sealed class SysLogHelper
    {
        static class Inner
        {
            internal static readonly SysLogHelper sysLogHelper = new SysLogHelper();
        }
        SysLogHelper() 
        {
            if (!WebLogHelper.StatusWait.VerifyStatus(WebLogStatus.Runed, () =>
            {
                DequeueLogToDB();
            })) return;
        }
        static internal SysLogHelper GetInstance() => Inner.sysLogHelper;
        ConcurrentQueue<SQLiteParameter[]> logRunQueue = new ConcurrentQueue<SQLiteParameter[]>();
        ConcurrentQueue<string> logs = new ConcurrentQueue<string>();
        internal async Task AppendSysLog(string msg, string v = null)//添加系统日志
        {
            append(msg, v);
        }
        void append(string msg, string v, bool isAddFile = true)
        {
            if (isAddFile)
            {
                logs.Enqueue(string.Format("{0}__ [{1}] ---> {2}", DateTime.Now.ToString("G"), v ?? WebLogHelper.curVersion, msg));
                appendToFile();
            }

            SQLiteParameter[] sQLiteParameters = new SQLiteParameter[3];
            sQLiteParameters[0] = new SQLiteParameter("V", v ?? WebLogHelper.curVersion);
            sQLiteParameters[1] = new SQLiteParameter("MSG", msg.Length > 250 ? msg.Substring(0, 250) : msg);
            sQLiteParameters[2] = new SQLiteParameter("Date", DateTime.Now);
            logRunQueue.Enqueue(sQLiteParameters);
            DequeueLogToDB();
        }
        volatile bool isRunAppendToFile = false;
        volatile bool isRunToDB = false;
        volatile int errorCount = 0;
        private void appendToFile()
        {
            if (isRunAppendToFile) return;
            isRunAppendToFile = true;
            bool isError = false;
            try
            {
                var basePath = System.Environment.CurrentDirectory + "\\weblog\\";
                int i = 0;
                while (i++ < errorCount)//对日志文件占用等等做一点处理
                    basePath = basePath + "_";
                var path = basePath + "log.txt";
                if (!File.Exists(path))
                {
                    try
                    {
                        if (!Directory.Exists(basePath))
                            Directory.CreateDirectory(basePath);
                        using (File.Create(path)) ;
                    }
                    catch (Exception ex)
                    {
                        isError = true;
                        append("创建文件异常:" + ex.Message, "", false);
                    }
                }
                using (FileStream mystream = new FileStream(path, FileMode.Append))
                using (StreamWriter myWrite = new StreamWriter(mystream))
                {
                    string s;
                    while (logs.TryDequeue(out s))
                        myWrite.WriteLine(s);
                }
            }
            catch (Exception ex)
            {
                isError = true;
                append("写文本数据异常:" + ex.Message, "", false);
            }
            finally
            {
                if (isError) errorCount++;
                isRunAppendToFile = false;
            }
        }
        private async Task DequeueLogToDB()//添加队列里的系统日志
        {
            if (isRunToDB) return;
            if (WebLogHelper.GetStatus() < WebLogStatus.Runed) return;
            isRunToDB = true;
            SQLiteParameter[] sQLiteParameters = null;
            while (logRunQueue.TryDequeue(out sQLiteParameters))
            {
                try
                {
                    await Comm.sqliteHelper.ExecuteNonQueryAsync($"insert into SysLog(V,MSG,Date) values(@V,@MSG,@Date)", false, sQLiteParameters);
                }
                catch (Exception ex)
                {
                    logRunQueue.Enqueue(sQLiteParameters);//失败了就再加回去
                    Thread.Sleep(2000);//延缓2秒 一般日志加失败了都是有锁
                    break;//有异常时跳出循环 下次加日志时再加
                }
            }
            isRunToDB = false;
        }

        internal object Searchs(SysLogDto dto)
        {
            SQLiteParameter[] sQLiteParameters = new SQLiteParameter[5];
            sQLiteParameters[0] = new SQLiteParameter("V", dto.V);
            sQLiteParameters[1] = new SQLiteParameter("MSG", "%" + (dto.MSG ?? "") + "%");
            sQLiteParameters[2] = new SQLiteParameter("Date", dto.Date);
            var commandStr = @"select * from SysLog where
                            (@Date is null or @Date='' or date>@Date) and
                            (@V is null or @V='' or V=@V) and
                            (@MSG='%%' or MSG like @MSG) and
                            1=1 ";
            string orderStr = "";
            if (dto.OrderField.HasValue)
            {
                if (!dto.IsDesc.HasValue) dto.IsDesc = true;
                orderStr += $"ORDER BY {dto.OrderField.ToString()} {(dto.IsDesc.Value ? "Desc" : "")}";
            }
            else orderStr += "ORDER BY date Desc ";//id正常情况下就是时间序列的
            orderStr += @" limit @Skip,@Take";
            //页码处理
            dto.Skip = dto.CurrentPageIndex * dto.PageSize;
            dto.Take = dto.PageSize;
            sQLiteParameters[3] = new SQLiteParameter("Skip", dto.Skip);
            sQLiteParameters[4] = new SQLiteParameter("Take", dto.Take);
            var dt = Comm.sqliteHelper.GetDataTable(commandStr + orderStr, sQLiteParameters: sQLiteParameters);
            var totalStr = Comm.sqliteHelper.GetDataTable(commandStr.Replace("select *", "select count(*)"), sQLiteParameters: sQLiteParameters).Rows[0][0].ToString();
            return new { total = int.Parse(totalStr), rows = dt };
        }
    }
}
